using System;
using System.Collections.Generic;
using System.Html;
using System.Xml;
using jQueryApi;
using Xrm;

namespace UIRecordCounter
{
    public class Counter
    {
        /// <summary>
        /// This method allows you to quickly display the record count of multiple navigation items.
        /// <example>
        /// <code>
        /// <![CDATA[
        /// UIRecordCounter.Counter.displayCount(['navActivities:activitypointer:regardingobjectid']);
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="locations">An array of navigation items.
        /// Syntax: ['navName:entity:foreignKeyField', 'navActivities:activitypointer:regardingobjectid']</param>
        public static void DisplayCount(string[] locations)
        {
            string id = Page.Data.Entity.GetId();

            if (locations != null && locations.Length > 0)
            {
                for (int i = 0; i < locations.Length; i++)
                {
                    string current = locations[i];
                    string[] parts = current.Split(':');

                    string navLabel = parts[0];
                    string entity = parts[1];
                    string fk = parts[2];

                    // only show the # of active records
                    string xml = WashWithSoap("<fetch mapping='logical' distinct='false' aggregate='true'><entity name='" + entity + "'><attribute name='" + fk + "' alias='count' aggregate='count' /><filter type='and'><condition attribute='statecode' operator='eq' value='0' /><condition attribute='" + fk + "' operator='eq' value='" + id + "' /></filter></entity></fetch>");

                    ExecuteCountRequest(navLabel, xml);
                }
            }
        }

        /// <summary>
        /// This method is useful for navigation items such as 'closed activities'. Closed Activities navigation filters based on a different statuscode so with this method you're free to specify additional conditions.
        /// 
        /// <example>
        /// <code>
        /// <![CDATA[
        /// UIRecordCounter.Counter.displayCountWithConditions("navActivities", "activitypointer", "regardingobjectid", "<condition attribute='statuscode' operator='eq' value='1'>");
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="navLabel">Name of the navigation item which can be used with Xrm.Page Navigation.Item.Get method.</param>
        /// <param name="entity">Schema name of the entity. eg: activitypointer.</param>
        /// <param name="foreignKeyField">Foreign key field of the entity. eg: regardingobjectid.</param>
        /// <param name="conditionsXml">Additional condition expressions that you'd like to filter by.</param>
        public static void DisplayCountWithConditions(string navLabel, string entity, string foreignKeyField, string conditionsXml)
        {
            string id = Page.Data.Entity.GetId();

            string xml = WashWithSoap("<fetch mapping='logical' distinct='false' aggregate='true'><entity name='" + entity + "'><attribute name='" + foreignKeyField + "' alias='count' aggregate='count' /><filter type='and'><condition attribute='" + foreignKeyField + "' operator='eq' value='" + id + "' />" + conditionsXml + "</filter></entity></fetch>");

            ExecuteCountRequest(navLabel, xml);
        }

        private static void ExecuteCountRequest(string navLabel, string xml)
        {
            jQueryAjaxOptions options = new jQueryAjaxOptions();

            options.Data = xml;
            options.Async = true;
            options.Url = GetCorrectedServerUrl() + "/xrmservices/2011/organization.svc/web";
            options.ContentType = "text/xml; charset=utf-8";
            options.Type = "POST";
            // todo: extend script# to add this as a property to jQueryAjaxOptions
            Script.Literal("{0}.headers = {{ SOAPAction: 'http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute' }}", options);

            options.Success = new AjaxRequestCallback(delegate(object data, string state, jQueryXmlHttpRequest request)
            {
                string key = "<b:key>count</b:key><b:value>";
                int start = request.ResponseText.IndexOf("<b:key>count</b:key><b:value>") + key.Length;
                int end = request.ResponseText.IndexOf("</b:value>", start);

                string value = request.ResponseText.Substring(start, end);

                /*XmlDocument document = jQuery.ParseXml(request.ResponseText);

                // todo: create script# request/response objects to parse these out
                XmlNode node = document.SelectSingleNode("//s:Envelope/s:Body/ExecuteResponse/ExecuteResult/a:Results/a:KeyValuePairOfstringanyType/b:value/a:Entities/a:Entity/a:Attributes/a:KeyValuePairOfstringanyType/b:value/a:Value");

                string value = node != null ? node.InnerText : "";*/

                SetLabel(navLabel, value);
            });

            jQuery.Ajax(options);
        }

        private static void SetLabel(string navLabel, string value)
        {
            if (value != "")
            {
                NavigationItem item = Page.Ui.Navigation.Items.Get(navLabel);
                string label = item.GetLabel();

                if (label.Length >= 21 - value.Length)
                {
                    label = label.Substring(0, (21 - value.Length - 3)) + "...";
                }

                item.SetLabel(label + " (" + value + ")");
            }
        }

        private static string WashWithSoap(string fetchXml)
        {
            // todo: potential for this to be done via script# classes rather than these magical strings
            string xml = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\"><request i:type=\"b:RetrieveMultipleRequest\" xmlns:b=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><b:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\"> <b:KeyValuePairOfstringanyType><c:key>Query</c:key><c:value i:type=\"b:FetchExpression\"><b:Query>";

            xml += Encode(fetchXml);

            xml += "</b:Query></c:value></b:KeyValuePairOfstringanyType></b:Parameters><b:RequestId i:nil=\"true\"/><b:RequestName>RetrieveMultiple</b:RequestName></request></Execute></s:Body></s:Envelope>";

            return xml;
        }

        private static string GetCorrectedServerUrl()
        {
            string sdkUrl = Page.Context.GetServerUrl(); // this gives a bad url, RU12 will have getClientUrl :)

            string[] partsWindow = Window.Location.Href.Split('/');
            string[] partsSdk = sdkUrl.Split('/');
            string correctedUrl = "";

            for (int i = 0; i < partsSdk.Length; i++)
            {
                if (i < 3) { correctedUrl += partsWindow[i] + "/"; } // this gets it until http://server:port
                else { correctedUrl += partsSdk[i] + "/"; }
            }

            return correctedUrl;
        }

        private static string Encode(string xml)
        {
            // todo: clean this up...
            Script.Literal("{0} = {0}.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")", xml);

            return xml;
        }
    }
}
